home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / memfile.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  27KB  |  1,126 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /* for debugging */
  10. #define CHECK(c, s)    if (c) printf(s)
  11.  
  12. /*
  13.  * memfile.c: Contains the functions for handling blocks of memory which can
  14.  * be stored in a file. This is the implementation of a sort of virtual memory.
  15.  *
  16.  * A memfile consists of a sequence of blocks. The blocks numbered from 0
  17.  * upwards have been assigned a place in the actual file. The block number
  18.  * is equal to the page number in the file. The
  19.  * blocks with negative numbers are currently in memory only. They can be
  20.  * assigned a place in the file when too much memory is being used. At that
  21.  * moment they get a new, positive, number. A list is used for translation of
  22.  * negative to positive numbers.
  23.  *
  24.  * The size of a block is a multiple of a page size, normally the page size of
  25.  * the device the file is on. Most blocks are 1 page long. A Block of multiple
  26.  * pages is used for a line that does not fit in a single page.
  27.  *
  28.  * Each block can be in memory and/or in a file. The blocks stays in memory
  29.  * as long as it is locked. If it is no longer locked it can be swapped out to
  30.  * the file. It is only written to the file if it has been changed.
  31.  *
  32.  * Under normal operation the file is created when opening the memory file and
  33.  * deleted when closing the memory file. Only with recovery an existing memory
  34.  * file is opened.
  35.  */
  36.  
  37. #ifdef MSDOS
  38. # include <io.h>        /* for lseek(), must be before vim.h */
  39. #endif
  40.  
  41. #include "vim.h"
  42. #include "globals.h"
  43. #include "proto.h"
  44. #include "param.h"
  45. #include <fcntl.h>
  46.  
  47. /* 
  48.  * Some systems have the page size in statfs, some in stat
  49.  */
  50. #if defined(SCO) || defined(_SEQUENT_) || defined(__sgi) || defined(MIPS) || defined(MIPSEB) || defined(m88k)
  51. # include <sys/types.h>
  52. # include <sys/statfs.h>
  53. # define STATFS statfs
  54. # define F_BSIZE f_bsize
  55. int  fstatfs __ARGS((int, struct statfs *, int, int));
  56. #else
  57. # define STATFS stat
  58. # define F_BSIZE st_blksize
  59. # define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
  60. #endif
  61.  
  62. /*
  63.  * for Amiga Dos 2.0x we use Flush
  64.  */
  65. #ifdef AMIGA
  66. # ifndef NO_ARP
  67. extern int dos2;                    /* this is in amiga.c */
  68. # endif
  69. # ifdef SASC
  70. #  include <proto/dos.h>
  71. #  include <ios1.h>                    /* for chkufb() */
  72. # endif
  73. #endif
  74.  
  75. #define MEMFILE_PAGE_SIZE 4096        /* default page size */
  76.  
  77. static long total_mem_used = 0;    /* total memory used for memfiles */
  78.  
  79. static void mf_ins_hash __ARGS((MEMFILE *, BHDR *));
  80. static void mf_rem_hash __ARGS((MEMFILE *, BHDR *));
  81. static BHDR *mf_find_hash __ARGS((MEMFILE *, blocknr_t));
  82. static void mf_ins_used __ARGS((MEMFILE *, BHDR *));
  83. static void mf_rem_used __ARGS((MEMFILE *, BHDR *));
  84. static BHDR *mf_release __ARGS((MEMFILE *, int));
  85. static BHDR *mf_alloc_bhdr __ARGS((MEMFILE *, int));
  86. static void mf_free_bhdr __ARGS((BHDR *));
  87. static void mf_ins_free __ARGS((MEMFILE *, BHDR *));
  88. static BHDR *mf_rem_free __ARGS((MEMFILE *));
  89. static int    mf_read __ARGS((MEMFILE *, BHDR *));
  90. static int    mf_write __ARGS((MEMFILE *, BHDR *));
  91. static int    mf_trans_add __ARGS((MEMFILE *, BHDR *));
  92. static void mf_do_open __ARGS((MEMFILE *, char_u *, int));
  93.  
  94. /*
  95.  * The functions for using a memfile:
  96.  *
  97.  * mf_open()        open a new or existing memfile
  98.  * mf_close()        close (and delete) a memfile
  99.  * mf_new()            create a new block in a memfile and lock it
  100.  * mf_get()            get an existing block and lock it
  101.  * mf_put()            unlock a block, may be marked for writing
  102.  * mf_free()        remove a block
  103.  * mf_sync()        sync changed parts of memfile to disk
  104.  * mf_release_all()    release as much memory as possible
  105.  * mf_trans_del()    may translate negative to positive block number
  106.  * mf_fullname()    make file name full path (use before first :cd)
  107.  */
  108.  
  109. /*
  110.  * mf_open: open an existing or new memory block file
  111.  *
  112.  *    fname:        name of file to use (NULL means no file at all)
  113.  *                Note: fname must have been allocated, it is not copied!
  114.  *                        If opening the file fails, fname is freed.
  115.  *  new:        if TRUE: file should be truncated when opening
  116.  *  fail_nofile:    if TRUE: if file cannot be opened, fail.
  117.  *
  118.  * return value: identifier for this memory block file.
  119.  */
  120.     MEMFILE *
  121. mf_open(fname, new, fail_nofile)
  122.     char_u    *fname;
  123.     int        new;
  124.     int        fail_nofile;
  125. {
  126.     MEMFILE            *mfp;
  127.     int                i;
  128.     long            size;
  129. #ifdef UNIX
  130.     struct STATFS     stf;
  131. #endif
  132.  
  133.     if ((mfp = (MEMFILE *)alloc((unsigned)sizeof(MEMFILE))) == NULL)
  134.     {
  135.         free(fname);
  136.         return NULL;
  137.     }
  138.  
  139.     if (fname == NULL)        /* no file for this memfile, use memory only */
  140.     {
  141.         mfp->mf_fname = NULL;
  142.         mfp->mf_xfname = NULL;
  143.         mfp->mf_fd = -1;
  144.     }
  145.     else
  146.         mf_do_open(mfp, fname, new);        /* try to open the file */
  147.  
  148.     /*
  149.      * if fail_nofile is set and there is no file, return here
  150.      */
  151.     if (mfp->mf_fd < 0 && fail_nofile)
  152.     {
  153.         free(mfp);
  154.         return NULL;
  155.     }
  156.  
  157.     mfp->mf_free_first = NULL;            /* free list is empty */
  158.     mfp->mf_used_first = NULL;            /* used list is empty */
  159.     mfp->mf_used_last = NULL;
  160.     mfp->mf_dirty = FALSE;
  161.     mfp->mf_used_count = 0;
  162.     for (i = 0; i < MEMHASHSIZE; ++i)
  163.     {
  164.         mfp->mf_hash[i] = NULL;            /* hash lists are empty */
  165.         mfp->mf_trans[i] = NULL;        /* trans lists are empty */
  166.     }
  167.     mfp->mf_page_size = MEMFILE_PAGE_SIZE;
  168.  
  169. #ifdef UNIX
  170.     /*
  171.      * Try to set the page size equal to the block size of the device.
  172.      * Speeds up I/O a lot.
  173.      * NOTE: minimal block size depends on size of block 0 data!
  174.      * The maximal block size is arbitrary.
  175.      */
  176.     if (mfp->mf_fd >= 0 &&
  177.                     fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0 &&
  178.                     stf.F_BSIZE >= 1048 && stf.F_BSIZE <= 50000)
  179.         mfp->mf_page_size = stf.F_BSIZE;
  180. #endif
  181.  
  182.     if (mfp->mf_fd < 0 || new || (size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
  183.         mfp->mf_blocknr_max = 0;        /* no file or empty file */
  184.     else
  185.         mfp->mf_blocknr_max = size / mfp->mf_page_size;
  186.     mfp->mf_blocknr_min = -1;
  187.     mfp->mf_neg_count = 0;
  188.     mfp->mf_infile_count = mfp->mf_blocknr_max;
  189.     if (mfp->mf_fd < 0)
  190.         mfp->mf_used_count_max = 0;            /* no limit */
  191.     else
  192.         mfp->mf_used_count_max = p_mm * 1024 / mfp->mf_page_size;
  193.  
  194.     return mfp;
  195. }
  196.  
  197. /*
  198.  * mf_open_file: open a file for an existing memfile. Used when updatecount
  199.  *                 set from 0 to some value.
  200.  *
  201.  *    fname:        name of file to use (NULL means no file at all)
  202.  *                Note: fname must have been allocated, it is not copied!
  203.  *                        If opening the file fails, fname is freed.
  204.  *
  205.  * return value: FAIL if file could not be opened, OK otherwise
  206.  */
  207.     int
  208. mf_open_file(mfp, fname)
  209.     MEMFILE        *mfp;
  210.     char_u        *fname;
  211. {
  212.     mf_do_open(mfp, fname, TRUE);                /* try to open the file */
  213.  
  214.     if (mfp->mf_fd < 0)
  215.         return FAIL;
  216.  
  217.     mfp->mf_dirty = TRUE;
  218.     return OK;
  219. }
  220.  
  221. /*
  222.  * close a memory file and delete the associated file if 'delete' is TRUE
  223.  */
  224.     void
  225. mf_close(mfp, delete)
  226.     MEMFILE    *mfp;
  227.     int        delete;
  228. {
  229.     BHDR        *hp, *nextp;
  230.     NR_TRANS    *tp, *tpnext;
  231.     int            i;
  232.  
  233.     if (mfp == NULL)                /* safety check */
  234.         return;
  235.     if (mfp->mf_fd >= 0)
  236.     {
  237.         if (close(mfp->mf_fd) < 0)
  238.             EMSG("Close error on swap file");
  239.     }
  240.     if (delete && mfp->mf_fname != NULL)
  241.         remove((char *)mfp->mf_fname);
  242.                                             /* free entries in used list */
  243.     for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
  244.     {
  245.         nextp = hp->bh_next;
  246.         mf_free_bhdr(hp);
  247.     }
  248.     while (mfp->mf_free_first != NULL)        /* free entries in free list */
  249.         (void)free(mf_rem_free(mfp));
  250.     for (i = 0; i < MEMHASHSIZE; ++i)        /* free entries in trans lists */
  251.         for (tp = mfp->mf_trans[i]; tp != NULL; tp = tpnext)
  252.         {
  253.             tpnext = tp->nt_next;
  254.             free(tp);
  255.         }
  256.     free(mfp->mf_fname);
  257.     free(mfp->mf_xfname);
  258.     free(mfp);
  259. }
  260.  
  261. /*
  262.  * get a new block
  263.  *
  264.  *   negative: TRUE if negative block number desired (data block)
  265.  */
  266.     BHDR *
  267. mf_new(mfp, negative, page_count)
  268.     MEMFILE        *mfp;
  269.     int            negative;
  270.     int            page_count;
  271. {
  272.     BHDR    *hp;            /* new BHDR */
  273.     BHDR    *freep;            /* first block in free list */
  274.     char_u    *p;
  275.  
  276.     /*
  277.      * If we reached the maximum size for the used memory blocks, release one
  278.      * If a BHDR is returned, use it and adjust the page_count if necessary.
  279.      */
  280.     hp = mf_release(mfp, page_count);
  281.  
  282. /*
  283.  * Decide on the number to use:
  284.  * If there is a free block, use its number.
  285.  * Otherwise use mf_block_min for a negative number, mf_block_max for
  286.  * a positive number.
  287.  */
  288.     freep = mfp->mf_free_first;
  289.     if (!negative && freep != NULL && freep->bh_page_count >= page_count)
  290.     {
  291.         /*
  292.          * If the block in the free list has more pages, take only the number
  293.          * of pages needed and allocate a new BHDR with data
  294.          *
  295.          * If the number of pages matches and mf_release did not return a BHDR,
  296.          * use the BHDR from the free list and allocate the data
  297.          *
  298.          * If the number of pages matches and mf_release returned a BHDR,
  299.          * just use the number and free the BHDR from the free list
  300.          */
  301.         if (freep->bh_page_count > page_count)
  302.         {
  303.             if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
  304.                 return NULL;
  305.             hp->bh_bnum = freep->bh_bnum;
  306.             freep->bh_bnum += page_count;
  307.             freep->bh_page_count -= page_count;
  308.         }
  309.         else if (hp == NULL)        /* need to allocate memory for this block */
  310.         {
  311.             if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
  312.                 return NULL;
  313.             hp = mf_rem_free(mfp);
  314.             hp->bh_data = p;
  315.         }
  316.         else                /* use the number, remove entry from free list */
  317.         {
  318.             freep = mf_rem_free(mfp);
  319.             hp->bh_bnum = freep->bh_bnum;
  320.             free(freep);
  321.         }
  322.     }
  323.     else        /* get a new number */
  324.     {
  325.         if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
  326.             return NULL;
  327.         if (negative)
  328.         {
  329.             hp->bh_bnum = mfp->mf_blocknr_min--;
  330.             mfp->mf_neg_count++;
  331.         }
  332.         else
  333.         {
  334.             hp->bh_bnum = mfp->mf_blocknr_max;
  335.             mfp->mf_blocknr_max += page_count;
  336.         }
  337.     }
  338.     hp->bh_flags = BH_LOCKED | BH_DIRTY;        /* new block is always dirty */
  339.     mfp->mf_dirty = TRUE;
  340.     hp->bh_page_count = page_count;
  341.     mf_ins_used(mfp, hp);
  342.     mf_ins_hash(mfp, hp);
  343.  
  344.     return hp;
  345. }
  346.  
  347. /*
  348.  * get existing block 'nr' with 'page_count' pages
  349.  *
  350.  * Note: The caller should first check a negative nr with mf_trans_del()
  351.  */
  352.     BHDR *
  353. mf_get(mfp, nr, page_count)
  354.     MEMFILE        *mfp;
  355.     blocknr_t    nr;
  356.     int            page_count;
  357. {
  358.     BHDR    *hp;
  359.                                                 /* doesn't exist */
  360.     if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
  361.         return NULL;
  362.  
  363.     /*
  364.      * see if it is in the cache
  365.      */
  366.     hp = mf_find_hash(mfp, nr);
  367.     if (hp == NULL)        /* not in the hash list */
  368.     {
  369.         if (nr < 0 || nr >= mfp->mf_infile_count)    /* can't be in the file */
  370.             return NULL;
  371.  
  372.         /* could check here if the block is in the free list */
  373.  
  374.         /*
  375.          * Check if we need to flush an existing block.
  376.          * If so, use that block.
  377.          * If not, allocate a new block.
  378.          */
  379.         hp = mf_release(mfp, page_count);
  380.         if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
  381.             return NULL;
  382.  
  383.         hp->bh_bnum = nr;
  384.         hp->bh_flags = 0;
  385.         hp->bh_page_count = page_count;
  386.         if (mf_read(mfp, hp) == FAIL)        /* cannot read the block! */
  387.         {
  388.             mf_free_bhdr(hp);
  389.             return NULL;
  390.         }
  391.     }
  392.     else
  393.     {
  394.         mf_rem_used(mfp, hp);    /* remove from list, insert in front below */
  395.         mf_rem_hash(mfp, hp);
  396.     }
  397.  
  398.     hp->bh_flags |= BH_LOCKED;
  399.     mf_ins_used(mfp, hp);        /* put in front of used list */
  400.     mf_ins_hash(mfp, hp);        /* put in front of hash list */
  401.  
  402.     return hp;
  403. }
  404.  
  405. /*
  406.  * release the block *hp
  407.  *
  408.  *   dirty: Block must be written to file later
  409.  *     infile: Block should be in file (needed for recovery)
  410.  *
  411.  *  no return value, function cannot fail
  412.  */
  413.     void
  414. mf_put(mfp, hp, dirty, infile)
  415.     MEMFILE    *mfp;
  416.     BHDR    *hp;
  417.     int        dirty;
  418.     int        infile;
  419. {
  420.     int        flags;
  421.  
  422.     flags = hp->bh_flags;
  423.     CHECK((flags & BH_LOCKED) == 0, "block was not locked");
  424.     flags &= ~BH_LOCKED;
  425.     if (dirty)
  426.     {
  427.         flags |= BH_DIRTY;
  428.         mfp->mf_dirty = TRUE;
  429.     }
  430.     hp->bh_flags = flags;
  431.     if (infile)
  432.         mf_trans_add(mfp, hp);        /* may translate negative in positive nr */
  433. }
  434.  
  435. /*
  436.  * block *hp is no longer in used, may put it in the free list of memfile *mfp
  437.  */
  438.     void
  439. mf_free(mfp, hp)
  440.     MEMFILE    *mfp;
  441.     BHDR    *hp;
  442. {
  443.     free(hp->bh_data);            /* free the memory */
  444.     mf_rem_hash(mfp, hp);        /* get *hp out of the hash list */
  445.     mf_rem_used(mfp, hp);        /* get *hp out of the used list */
  446.     if (hp->bh_bnum < 0)
  447.     {
  448.         free(hp);                /* don't want negative numbers in free list */
  449.         mfp->mf_neg_count--;
  450.     }
  451.     else
  452.         mf_ins_free(mfp, hp);    /* put *hp in the free list */
  453. }
  454.  
  455. /*
  456.  * sync the memory file *mfp to disk
  457.  *    if 'all' is FALSE blocks with negative numbers are not synced, even when
  458.  *  they are dirty!
  459.  *  if 'check_char' is TRUE, stop syncing when a character becomes available,
  460.  *  but sync at least one block.
  461.  *
  462.  * Return FAIL for failure, OK otherwise
  463.  */
  464.     int
  465. mf_sync(mfp, all, check_char)
  466.     MEMFILE    *mfp;
  467.     int        all;
  468.     int        check_char;
  469. {
  470.     int        status;
  471.     BHDR    *hp;
  472. #if defined(MSDOS) || defined(SCO)
  473.     int        fd;
  474. #endif
  475.  
  476.     if (mfp->mf_fd < 0)        /* there is no file, nothing to do */
  477.     {
  478.         mfp->mf_dirty = FALSE;
  479.         return FAIL;
  480.     }
  481.  
  482.     /*
  483.      * sync from last to first (may reduce the probability of an inconsistent file)
  484.      * If a write fails, it is very likely caused by a full filesystem. Then we
  485.      * only try to write blocks within the existing file. If that also fails then
  486.      * we give up.
  487.      */
  488.     status = OK;
  489.     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
  490.         if ((all || hp->bh_bnum >= 0) && (hp->bh_flags & BH_DIRTY) &&
  491.                     (status == OK || (hp->bh_bnum >= 0 &&
  492.                         hp->bh_bnum < mfp->mf_infile_count)))
  493.         {
  494.             if (mf_write(mfp, hp) == FAIL)
  495.             {
  496.                 if (status == FAIL)        /* double error: quit syncing */
  497.                     break;
  498.                 status = FAIL;
  499.             }
  500.             if (check_char && mch_char_avail())    /* char available now */
  501.                 break;
  502.         }
  503.     
  504.     /*
  505.      * If the whole list is flushed, the memfile is not dirty anymore.
  506.      * In case of an error this flag is also set, to avoid trying all the time.
  507.      */
  508.     if (hp == NULL || status == FAIL)
  509.         mfp->mf_dirty = FALSE;
  510.  
  511. #if defined(UNIX) && !defined(SCO)
  512. # if !defined(SVR4) && (defined(MIPS) || defined(MIPSEB) || defined(m88k))         
  513.      sync();         /* Do we really need to sync()?? (jw) */   
  514. # else
  515.     /*
  516.      * Unix has the very useful fsync() function, just what we need.
  517.      */
  518.     if (fsync(mfp->mf_fd))
  519.         status = FAIL;
  520. # endif
  521. #endif
  522. #if defined(MSDOS) || defined(SCO)
  523.     /*
  524.      * MSdos is a bit more work: Duplicate the file handle and close it.
  525.      * This should flush the file to disk.
  526.      * Also use this for SCO, which has no fsync().
  527.      */
  528.     if ((fd = dup(mfp->mf_fd)) >= 0)
  529.         close(fd);
  530. #endif
  531. #ifdef AMIGA
  532.     /*
  533.      * Flush() only exists for AmigaDos 2.0.
  534.      * For 1.3 it should be done with close() + open(), but then the risk
  535.      * is that the open() may fail and loose the file....
  536.      */
  537. # ifndef NO_ARP
  538.     if (dos2)
  539. # endif
  540. # ifdef SASC
  541.     {
  542.         struct UFB *fp = chkufb(mfp->mf_fd);
  543.  
  544.         if (fp != NULL)
  545.             Flush(fp->ufbfh);
  546.     }
  547. # else
  548.         Flush(_devtab[mfp->mf_fd].fd);
  549. # endif
  550. #endif
  551.  
  552.     return status;
  553. }
  554.  
  555. /*
  556.  * insert block *hp in front of hashlist of memfile *mfp
  557.  */
  558.     static void
  559. mf_ins_hash(mfp, hp)
  560.     MEMFILE    *mfp;
  561.     BHDR    *hp;
  562. {
  563.     BHDR    *hhp;
  564.     int        hash;
  565.  
  566.     hash = MEMHASH(hp->bh_bnum);
  567.     hhp = mfp->mf_hash[hash];
  568.     hp->bh_hash_next = hhp;
  569.     hp->bh_hash_prev = NULL;
  570.     if (hhp != NULL)
  571.         hhp->bh_hash_prev = hp;
  572.     mfp->mf_hash[hash] = hp;
  573. }
  574.  
  575. /*
  576.  * remove block *hp from hashlist of memfile list *mfp
  577.  */
  578.     static void
  579. mf_rem_hash(mfp, hp)
  580.     MEMFILE    *mfp;
  581.     BHDR    *hp;
  582. {
  583.     if (hp->bh_hash_prev == NULL)
  584.         mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
  585.     else
  586.         hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
  587.     
  588.     if (hp->bh_hash_next)
  589.         hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
  590. }
  591.  
  592. /*
  593.  * look in hash lists of memfile *mfp for block header with number 'nr'
  594.  */
  595.     static BHDR *
  596. mf_find_hash(mfp, nr)
  597.     MEMFILE        *mfp;
  598.     blocknr_t    nr;
  599. {
  600.     BHDR        *hp;
  601.  
  602.     for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
  603.         if (hp->bh_bnum == nr)
  604.             break;
  605.     return hp;
  606. }
  607.  
  608. /*
  609.  * insert block *hp in front of used list of memfile *mfp
  610.  */
  611.     static void
  612. mf_ins_used(mfp, hp)
  613.     MEMFILE    *mfp;
  614.     BHDR    *hp;
  615. {
  616.     hp->bh_next = mfp->mf_used_first;
  617.     mfp->mf_used_first = hp;
  618.     hp->bh_prev = NULL;
  619.     if (hp->bh_next == NULL)        /* list was empty, adjust last pointer */
  620.         mfp->mf_used_last = hp;
  621.     else
  622.         hp->bh_next->bh_prev = hp;
  623.     mfp->mf_used_count += hp->bh_page_count;
  624.     total_mem_used += hp->bh_page_count * mfp->mf_page_size;
  625. }
  626.  
  627. /*
  628.  * remove block *hp from used list of memfile *mfp
  629.  */
  630.     static void
  631. mf_rem_used(mfp, hp)
  632.     MEMFILE    *mfp;
  633.     BHDR    *hp;
  634. {
  635.     if (hp->bh_next == NULL)        /* last block in used list */
  636.         mfp->mf_used_last = hp->bh_prev;
  637.     else
  638.         hp->bh_next->bh_prev = hp->bh_prev;
  639.     if (hp->bh_prev == NULL)        /* first block in used list */
  640.         mfp->mf_used_first = hp->bh_next;
  641.     else
  642.         hp->bh_prev->bh_next = hp->bh_next;
  643.     mfp->mf_used_count -= hp->bh_page_count;
  644.     total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
  645. }
  646.  
  647. /*
  648.  * Release the least recently used block from the used list if the number
  649.  * of used memory blocks gets to big.
  650.  *
  651.  * Return the block header to the caller, including the memory block, so
  652.  * it can be re-used. Make sure the page_count is right.
  653.  */
  654.     static BHDR *
  655. mf_release(mfp, page_count)
  656.     MEMFILE        *mfp;
  657.     int            page_count;
  658. {
  659.     BHDR        *hp;
  660.  
  661.         /*
  662.          * don't release a block if
  663.          *        there is no file for this memfile
  664.          * or
  665.          *         there is no limit to the number of blocks for this memfile or
  666.          *        the maximum is not reached yet
  667.          *    and
  668.          *        total memory used is not up to 'maxmemtot'
  669.          */
  670.     if (mfp->mf_fd < 0 || ((mfp->mf_used_count < mfp->mf_used_count_max ||
  671.                         mfp->mf_used_count_max == 0) &&
  672.                         (total_mem_used >> 10) < p_mmt))
  673.         return NULL;
  674.  
  675.     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
  676.         if (!(hp->bh_flags & BH_LOCKED))
  677.             break;
  678.     if (hp == NULL)        /* not a single one that can be released */
  679.         return NULL;
  680.  
  681.         /*
  682.          * If the block is dirty, write it.
  683.          * If the write fails we don't free it.
  684.          */
  685.     if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
  686.         return NULL;
  687.  
  688.     mf_rem_used(mfp, hp);
  689.     mf_rem_hash(mfp, hp);
  690.  
  691. /*
  692.  * If a BHDR is returned, make sure that the page_count of bh_data is right
  693.  */
  694.     if (hp->bh_page_count != page_count)
  695.     {
  696.         free(hp->bh_data);
  697.         if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
  698.         {
  699.             free(hp);
  700.             return NULL;
  701.         }
  702.         hp->bh_page_count = page_count;
  703.     }
  704.     return hp;
  705. }
  706.  
  707. /*
  708.  * release as many blocks as possible
  709.  * Used in case of out of memory
  710.  *
  711.  * return TRUE if any memory was released
  712.  */
  713.     int
  714. mf_release_all()
  715. {
  716.     BUF            *buf;
  717.     MEMFILE        *mfp;
  718.     BHDR        *hp;
  719.     int            retval = FALSE;
  720.  
  721.     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
  722.     {
  723.         mfp = buf->b_ml.ml_mfp;
  724.         if (mfp != NULL && mfp->mf_fd >= 0)        /* only if there is a memfile with a file */
  725.             for (hp = mfp->mf_used_last; hp != NULL; )
  726.             {
  727.                 if (!(hp->bh_flags & BH_LOCKED) &&
  728.                         (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL))
  729.                 {
  730.                     mf_rem_used(mfp, hp);
  731.                     mf_rem_hash(mfp, hp);
  732.                     mf_free_bhdr(hp);
  733.                     hp = mfp->mf_used_last;        /* re-start, list was changed */
  734.                     retval = TRUE;
  735.                 }
  736.                 else
  737.                     hp = hp->bh_prev;
  738.             }
  739.     }
  740.     return retval;
  741. }
  742.  
  743. /*
  744.  * Allocate a block header and a block of memory for it
  745.  */
  746.     static BHDR *
  747. mf_alloc_bhdr(mfp, page_count)
  748.     MEMFILE        *mfp;
  749.     int            page_count;
  750. {
  751.     BHDR    *hp;
  752.  
  753.     if ((hp = (BHDR *)alloc((unsigned)sizeof(BHDR))) != NULL)
  754.     {
  755.         if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
  756.         {
  757.             free(hp);            /* not enough memory */
  758.             hp = NULL;
  759.         }
  760.         hp->bh_page_count = page_count;
  761.     }
  762.     return hp;
  763. }
  764.  
  765. /*
  766.  * Free a block header and the block of memory for it
  767.  */
  768.     static void
  769. mf_free_bhdr(hp)
  770.     BHDR        *hp;
  771. {
  772.     free(hp->bh_data);
  773.     free(hp);
  774. }
  775.  
  776. /*
  777.  * insert entry *hp in the free list
  778.  */
  779.     static void
  780. mf_ins_free(mfp, hp)
  781.     MEMFILE    *mfp;
  782.     BHDR    *hp;
  783. {
  784.     hp->bh_next = mfp->mf_free_first;
  785.     mfp->mf_free_first = hp;
  786. }
  787.  
  788. /*
  789.  * remove the first entry from the free list and return a pointer to it
  790.  * Note: caller must check that mfp->mf_free_first is not NULL!
  791.  */
  792.     static BHDR *
  793. mf_rem_free(mfp)
  794.     MEMFILE    *mfp;
  795. {
  796.     BHDR    *hp;
  797.  
  798.     hp = mfp->mf_free_first;
  799.     mfp->mf_free_first = hp->bh_next;
  800.     return hp;
  801. }
  802.  
  803. /*
  804.  * read a block from disk
  805.  * 
  806.  * Return FAIL for failure, OK otherwise
  807.  */
  808.     static int
  809. mf_read(mfp, hp)
  810.     MEMFILE        *mfp;
  811.     BHDR        *hp;
  812. {
  813.     long_u        offset;
  814.     unsigned    page_size;
  815.     unsigned    size;
  816.  
  817.     if (mfp->mf_fd < 0)        /* there is no file, can't read */
  818.         return FAIL;
  819.  
  820.     page_size = mfp->mf_page_size;
  821.     offset = page_size * hp->bh_bnum;
  822.     size = page_size * hp->bh_page_count;
  823.     if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
  824.     {
  825.         EMSG("Seek error in swap file read");
  826.         return FAIL;
  827.     }
  828.     if (read(mfp->mf_fd, hp->bh_data, (size_t)size) != size)
  829.     {
  830.         EMSG("Read error in swap file");
  831.         return FAIL;
  832.     }
  833.     return OK;
  834. }
  835.  
  836. /*
  837.  * write a block to disk
  838.  * 
  839.  * Return FAIL for failure, OK otherwise
  840.  */
  841.     static int
  842. mf_write(mfp, hp)
  843.     MEMFILE        *mfp;
  844.     BHDR        *hp;
  845. {
  846.     long_u        offset;        /* offset in the file */
  847.     blocknr_t    nr;            /* block nr which is being written */
  848.     BHDR        *hp2;
  849.     unsigned    page_size;    /* number of bytes in a page */
  850.     unsigned    page_count;    /* number of pages written */
  851.     unsigned    size;        /* number of bytes written */
  852.  
  853.     if (mfp->mf_fd < 0)        /* there is no file, can't write */
  854.         return FAIL;
  855.  
  856.     if (hp->bh_bnum < 0)        /* must assign file block number */
  857.         if (mf_trans_add(mfp, hp) == FAIL)
  858.             return FAIL;
  859.  
  860.     page_size = mfp->mf_page_size;
  861.  
  862.     /*
  863.      * We don't want gaps in the file. Write the blocks in front of *hp
  864.      * to extend the file.
  865.      * If block 'mf_infile_count' is not in the hash list, it has been
  866.      * freed. Fill the space in the file with data from the current block.
  867.      */
  868.     for (;;)
  869.     {
  870.         nr = hp->bh_bnum;
  871.         if (nr > mfp->mf_infile_count)            /* beyond end of file */
  872.         {
  873.             nr = mfp->mf_infile_count;
  874.             hp2 = mf_find_hash(mfp, nr);        /* NULL catched below */
  875.         }
  876.         else
  877.             hp2 = hp;
  878.  
  879.         offset = page_size * nr;
  880.         if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
  881.         {
  882.             EMSG("Seek error in swap file write");
  883.             return FAIL;
  884.         }
  885.         if (hp2 == NULL)            /* freed block, fill with dummy data */
  886.             page_count = 1;
  887.         else
  888.             page_count = hp2->bh_page_count;
  889.         size = page_size * page_count;
  890.         if (write(mfp->mf_fd, (hp2 == NULL ? hp : hp2)->bh_data,
  891.                                                     (size_t)size) != size)
  892.         {
  893.             EMSG("Write error in swap file");
  894.             return FAIL;
  895.         }
  896.         if (hp2 != NULL)                    /* written a non-dummy block */
  897.             hp2->bh_flags &= ~BH_DIRTY;
  898.  
  899.         if (nr + page_count > mfp->mf_infile_count)        /* appended to the file */
  900.             mfp->mf_infile_count = nr + page_count;
  901.         if (nr == hp->bh_bnum)                /* written the desired block */
  902.             break;
  903.         nr += page_count;
  904.     }
  905.     return OK;
  906. }
  907.  
  908. /*
  909.  * Make block number for *hp positive and add it to the translation list
  910.  * 
  911.  * Return FAIL for failure, OK otherwise
  912.  */
  913.     static int
  914. mf_trans_add(mfp, hp)
  915.     MEMFILE    *mfp;
  916.     BHDR    *hp;
  917. {
  918.     BHDR        *freep;
  919.     blocknr_t    new;
  920.     int            hash;
  921.     NR_TRANS    *np;
  922.     int            page_count;
  923.  
  924.     if (hp->bh_bnum >= 0)                    /* it's already positive */
  925.         return OK;
  926.  
  927.     if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
  928.         return FAIL;
  929.  
  930. /*
  931.  * get a new number for the block.
  932.  * If the first item in the free list has sufficient pages, use its number
  933.  * Otherwise use mf_blocknr_max.
  934.  */
  935.     freep = mfp->mf_free_first;
  936.     page_count = hp->bh_page_count;
  937.     if (freep != NULL && freep->bh_page_count >= page_count)
  938.     {
  939.         new = freep->bh_bnum;
  940.         /*
  941.          * If the page count of the free block was larger, recude it.
  942.          * If the page count matches, remove the block from the free list
  943.          */
  944.         if (freep->bh_page_count > page_count)
  945.         {
  946.             freep->bh_bnum += page_count;
  947.             freep->bh_page_count -= page_count;
  948.         }
  949.         else
  950.         {
  951.             freep = mf_rem_free(mfp);
  952.             free(freep);
  953.         }
  954.     }
  955.     else
  956.     {
  957.         new = mfp->mf_blocknr_max;
  958.         mfp->mf_blocknr_max += page_count;
  959.     }
  960.  
  961.     np->nt_old_bnum = hp->bh_bnum;            /* adjust number */
  962.     np->nt_new_bnum = new;
  963.  
  964.     mf_rem_hash(mfp, hp);                    /* remove from old hash list */
  965.     hp->bh_bnum = new;
  966.     mf_ins_hash(mfp, hp);                    /* insert in new hash list */
  967.  
  968.     hash = MEMHASH(np->nt_old_bnum);        /* insert in trans list */
  969.     np->nt_next = mfp->mf_trans[hash];
  970.     mfp->mf_trans[hash] = np;
  971.     if (np->nt_next != NULL)
  972.         np->nt_next->nt_prev = np;
  973.     np->nt_prev = NULL;    
  974.  
  975.     return OK;
  976. }
  977.  
  978. /*
  979.  * Lookup a tranlation from the trans lists and delete the entry
  980.  * 
  981.  * Return the positive new number when found, the old number when not found
  982.  */
  983.      blocknr_t
  984. mf_trans_del(mfp, old)
  985.     MEMFILE        *mfp;
  986.     blocknr_t    old;
  987. {
  988.     int            hash;
  989.     NR_TRANS    *np;
  990.     blocknr_t    new;
  991.  
  992.     hash = MEMHASH(old);
  993.     for (np = mfp->mf_trans[hash]; np != NULL; np = np->nt_next)
  994.         if (np->nt_old_bnum == old)
  995.             break;
  996.     if (np == NULL)                /* not found */
  997.         return old;
  998.  
  999.     mfp->mf_neg_count--;
  1000.     new = np->nt_new_bnum;
  1001.     if (np->nt_prev != NULL)            /* remove entry from the trans list */
  1002.         np->nt_prev->nt_next = np->nt_next;
  1003.     else
  1004.         mfp->mf_trans[hash] = np->nt_next;
  1005.     if (np->nt_next != NULL)
  1006.         np->nt_next->nt_prev = np->nt_prev;
  1007.     free(np);
  1008.  
  1009.     return new;
  1010. }
  1011.  
  1012. /*
  1013.  * Make the name of the file used for the memfile a full path.
  1014.  * Used before doing a :cd
  1015.  */
  1016.     void
  1017. mf_fullname(mfp)
  1018.     MEMFILE        *mfp;
  1019. {
  1020.     if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_xfname != NULL)
  1021.     {
  1022.         free(mfp->mf_fname);
  1023.         mfp->mf_fname = mfp->mf_xfname;
  1024.         mfp->mf_xfname = NULL;
  1025.     }
  1026. }
  1027.  
  1028. /*
  1029.  * return TRUE if there are any translations pending for 'mfp'
  1030.  */
  1031.     int
  1032. mf_need_trans(mfp)
  1033.     MEMFILE        *mfp;
  1034. {
  1035.     return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
  1036. }
  1037.  
  1038. #if 1            /* included for beta release, TODO: remove later */
  1039. /*
  1040.  * print statistics for a memfile (for debugging)
  1041.  */
  1042.     void
  1043. mf_statistics()
  1044. {
  1045.     MEMFILE        *mfp;
  1046.     BHDR        *hp;
  1047.     int            used = 0;
  1048.     int            locked = 0;
  1049.     int            dirty = 0;
  1050.     int            nfree = 0;
  1051.     int            negative = 0;
  1052.  
  1053.     mfp = curbuf->b_ml.ml_mfp;
  1054.     if (mfp == NULL)
  1055.         MSG("No memfile");
  1056.     else
  1057.     {
  1058.         for (hp = mfp->mf_used_first; hp != NULL; hp = hp->bh_next)
  1059.         {
  1060.             ++used;
  1061.             if (hp->bh_flags & BH_LOCKED)
  1062.                 ++locked;
  1063.             if (hp->bh_flags & BH_DIRTY)
  1064.                 ++dirty;
  1065.             if (hp->bh_bnum < 0)
  1066.                 ++negative;
  1067.         }
  1068.         for (hp = mfp->mf_free_first; hp != NULL; hp = hp->bh_next)
  1069.             ++nfree;
  1070.         sprintf((char *)IObuff, "%d used (%d locked, %d dirty, %d (%d) negative), %d free",
  1071.                         used, locked, dirty, negative, (int)mfp->mf_neg_count, nfree);
  1072.         msg(IObuff);
  1073.     }
  1074. }
  1075. #endif
  1076.  
  1077. /*
  1078.  * open a file for a memfile
  1079.  */
  1080.     static void
  1081. mf_do_open(mfp, fname, new)
  1082.     MEMFILE        *mfp;
  1083.     char_u        *fname;
  1084.     int            new;
  1085. {
  1086.     mfp->mf_fname = fname;
  1087.     /*
  1088.      * get the full path name before the open, because this is
  1089.      * not possible after the open on the Amiga.
  1090.      * fname cannot be NameBuff, because it has been allocated.
  1091.      */
  1092.     if (FullName(fname, NameBuff, MAXPATHL) == FAIL)
  1093.         mfp->mf_xfname = NULL;
  1094.     else
  1095.         mfp->mf_xfname = strsave(NameBuff);
  1096.  
  1097.     /*
  1098.      * try to open the file
  1099.      */
  1100.     mfp->mf_fd = open((char *)fname, new ? (O_CREAT | O_RDWR | O_TRUNC) : (O_RDONLY)
  1101.  
  1102. #ifdef AMIGA                /* Amiga has no mode argument */
  1103.                     );
  1104. #endif
  1105. #ifdef UNIX                    /* open in rw------- mode */
  1106. # ifdef SCO
  1107.                     , (mode_t)0600);
  1108. # else
  1109.                     , 0600);
  1110. # endif
  1111. #endif
  1112. #ifdef MSDOS                /* open read/write */
  1113.                     , S_IREAD | S_IWRITE);
  1114. #endif
  1115.     /*
  1116.      * If the file cannot be opened, use memory only
  1117.      */
  1118.     if (mfp->mf_fd < 0)
  1119.     {
  1120.         free(fname);
  1121.         free(mfp->mf_xfname);
  1122.         mfp->mf_fname = NULL;
  1123.         mfp->mf_xfname = NULL;
  1124.     }
  1125. }
  1126.